#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <set>
namespace bitrpc
{
    namespace server
    {
        // 注册中心需要对服务提供者进行管理，根据先描述再组织，要先对服务提供者进行描述
        class ProviderManager
        {
        public:
            using ptr = std::shared_ptr<ProviderManager>;

            struct Provider // 服务提供者的描述对象
            {
                using ptr = std::shared_ptr<Provider>;
                Address host; // 主机信息，ip:port
                std::mutex _mutex;
                std::vector<std::string> methods; // 该提供者所能提供的服务
                BaseConnection::ptr conn;

                Provider(const BaseConnection::ptr &c, const Address &h)
                    : conn(c), host(h)
                {
                }
                void appendMethod(const string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    methods.emplace_back(method);
                }
            };
            // 当一个新的服务提供者进行服务注册的时候调用
            Provider::ptr addProvider(const BaseConnection::ptr &conn, const Address &host, const std::string &method)
            {
                DLOG("服务上线，新增服务提供者");

                Provider::ptr provider;
                {
                    std::unique_lock<std::mutex> lock(_mutex);

                    // 查找连接所关联的服务提供者对象，找到则获取，找不到则创建，并建立关联
                    if (_conns.count(conn))
                    {
                        provider = _conns[conn];
                    }
                    else
                    {
                        provider = std::make_shared<Provider>(conn, host);
                        _conns[conn] = provider;
                    }
                    // method方法的提供主机要多出一个，_providers新增数据
                    auto &providers = _providers[method];
                    providers.insert(provider);
                }
                // 向服务对象中新增一个所能提供的服务名称
                provider->appendMethod(method);
                return provider;
            }
            // 当一个服务提供者断开连接的时候，获取他的信息，用于进行下线通知
            Provider::ptr getProvider(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (_conns.count(conn) == 0)
                {
                    DLOG("没有服务提供者");
                    return Provider::ptr();
                }
                return _conns[conn];
            }
            // 当一个服务提供者断开连接的时候，删除它的关联信息
            void delProvider(const BaseConnection::ptr &conn)
            {
                DLOG("服务下线，删除服务提供者");
                std::unique_lock<std::mutex> lock(_mutex);
                // 当前断开连接的不是一个服务提供者
                if (_conns.count(conn) == 0)
                    return;
                // 如果是提供者，看看提供了什么服务，从服务者提供信息中删除服务提供者
                for (auto &method : _conns[conn]->methods)
                {
                    _providers[method].erase(_conns[conn]);
                }
                // 删除连接与服务提供者的关联关系
                _conns.erase(conn);
            }
            std::vector<Address> methodHosts(const string &method)
            {
                std::unique_lock<std::mutex> lock(_mutex);

                auto it = _providers.find(method);
                if (it == _providers.end())
                {
                    return std::vector<Address>();
                }
                std::vector<Provider::ptr> ret(it->second.begin(), it->second.end());
                std::vector<Address> ans;
                for (auto &addr : ret)
                {
                    ans.push_back(addr->host);
                }
                return ans;
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Provider::ptr>> _providers; // 某个服务有哪些主机能提供
            std::unordered_map<BaseConnection::ptr, Provider::ptr> _conns;       // 某个连接所对应的服务提供者
        };

        class DiscoverManager
        {
        public:
            using ptr = std::shared_ptr<DiscoverManager>;
            struct Discoverer // 服务发现者
            {
                using ptr = std::shared_ptr<Discoverer>;
                std::mutex _mutex;
                BaseConnection::ptr conn;         // 发现者关联的客户端连接
                std::vector<std::string> methods; // 发现过的服务名称

                Discoverer(const BaseConnection::ptr &c) : conn(c)
                {
                }
                void appendMethod(const string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    methods.push_back(method);
                }
            };
            // 当客户端进行服务发现的时候，新增发现者和服务名称
            Discoverer::ptr addDiscoverer(const BaseConnection::ptr &c, const std::string &method)
            {
                Discoverer::ptr discover;
                {
                    std::unique_lock<std::mutex> lock(_mutex);

                    // 查找连接所关联的服务提供者对象，找到则获取，找不到则创建，并建立关联
                    if (_conns.count(c))
                    {
                        discover = _conns[c];
                    }
                    else
                    {
                        discover = std::make_shared<Discoverer>(c);
                        _conns[c] = discover;
                    }
                    // method方法的发现主机要多出一个，_discovers新增数据
                    auto &providers = _discovers[method];
                    providers.insert(discover);
                }
                // 向服务对象中新增一个发现过的服务名称
                discover->appendMethod(method);
                return discover;
            }
            // 发现客户端断开连接时，找到发现者信息，删除关联数据
            void delDiscoverer(const BaseConnection::ptr &c)
            {
                DLOG("服务下线，删除服务发现者");

                std::unique_lock<std::mutex> lock(_mutex);
                // 当前断开连接的不是一个服务发现者
                if (_conns.count(c) == 0)
                    return;
                // 如果是发现者，看看发现了什么服务，从发现者发现信息中删除服务发现者
                for (auto &method : _conns[c]->methods)
                {
                    _discovers[method].erase(_conns[c]);
                }
                // 删除连接与服务发现者的关联关系
                _conns.erase(c);
            }
            // 当有新的服务提供者上线，则进行上线通知
            void onlineNotify(const std::string &method, const Address &host)
            {
                Notify(method, host, ServiceOptype::SERVICE_ONLINE);
            }
            // 当有新的服务提供者上线，则进行下线通知
            void offlineNotify(const std::string &method, const Address &host)
            {
                DLOG("%s 服务下线", method.c_str());
                Notify(method, host, ServiceOptype::SERVICE_OFFLINE);
            }

        private:
            void Notify(const std::string &method, const Address &host, ServiceOptype optype)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (_discovers.count(method) == 0)
                {
                    // 该服务没有发现者，不需要通知
                    return;
                }
                auto req_msg = MessageFactory::create<ServiceRequest>();
                req_msg->SetId(UUID::uuid());
                req_msg->SetMessageType(MessageType::REQ_SERVICE);
                req_msg->setMethod(method);
                req_msg->setServiceOptype(optype);
                req_msg->setHost(host);

                // 找到该服务的所有发现者，通知它们服务上线
                for (auto &discoverer : _discovers[method])
                {
                    discoverer->conn->send(req_msg);
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Discoverer::ptr>> _discovers; // 某个服务被哪些主机发现过
            std::unordered_map<BaseConnection::ptr, Discoverer::ptr> _conns;       // 哪个连接对应哪个服务发现者
        };

        class PDManager
        {
        public:
            using ptr = std::shared_ptr<PDManager>;
            PDManager() : _providers(std::make_shared<ProviderManager>()),
                          _discovers(std::make_shared<DiscoverManager>())
            {
            }
            // 注册到dispatcher
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                // 服务操作请求：1。服务注册 2.服务发现
                // 先判断是哪一种操作类型
                ServiceOptype optype = msg->serviceOptype();
                if (optype == ServiceOptype::SERVICE_REGISTRY)
                {
                    // 1.服务注册：
                    // （1）新增服务提供者 （2）进行服务上线的通知
                    ILOG("%s:%d 注册服务 %s", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                    _providers->addProvider(conn, msg->host(), msg->method());
                    auto provider = _providers->getProvider(conn);
                    // if (provider.get())
                    // {
                    //     // 有服务提供者,进行下线通知
                    //     DLOG("服务上线成功");
                    // }
                    _discovers->onlineNotify(msg->method(), msg->host());
                    registryResponse(conn, msg);
                }
                else if (optype == ServiceOptype::SERVICE_DISCOVERY)
                {
                    // 服务发现：
                    // 新增发现者

                    ILOG("客户端要进行%s服务发现！", msg->method().c_str());
                    _discovers->addDiscoverer(conn, msg->method());
                    discoverResponse(conn, msg);
                }
                else
                {
                    ELOG("收到服务操作请求，但是操作类型错误！");
                    errorResponse(conn, msg);
                }
            }
            // 连接断开，
            void onConnShutdown(const BaseConnection::ptr &conn)
            {
                auto provider = _providers->getProvider(conn);
                if (provider.get())
                {
                    // 有服务提供者,进行下线通知
                    ILOG("%s:%d 服务下线", provider->host.first.c_str(), provider->host.second);

                    for (auto &method : provider->methods)
                    {
                        _discovers->offlineNotify(method, provider->host);
                    }
                    _providers->delProvider(conn);
                }
                _discovers->delDiscoverer(conn);
            }

        private:
            void errorResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->SetId(msg->rid());
                rsp->SetMessageType(MessageType::RSP_SERVICE);
                rsp->setRcode(RCODE::RCODE_INVALID_OPTYPE);
                rsp->setServiceOptype(ServiceOptype::SERVICE_UNKNOW);
                conn->send(rsp);
            }
            // 对于服务注册的响应

            void registryResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->SetId(msg->rid());
                rsp->SetMessageType(MessageType::RSP_SERVICE);
                rsp->setRcode(RCODE::RCODE_OK);
                rsp->setServiceOptype(ServiceOptype::SERVICE_REGISTRY);
                conn->send(rsp);
            }
            // 对于服务发现的响应
            void discoverResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                auto rsp = MessageFactory::create<ServiceResponse>();
                rsp->SetId(msg->rid());
                rsp->SetMessageType(MessageType::RSP_SERVICE);
                rsp->setServiceOptype(ServiceOptype::SERVICE_DISCOVERY);

                std::vector<Address> hosts = _providers->methodHosts(msg->method());
                if (hosts.empty())
                { // 没有该方法的提供者
                    rsp->setRcode(RCODE::RCODE_NOT_FOUND_SERVICE);
                    return conn->send(rsp);
                }
                rsp->setRcode(RCODE::RCODE_OK);
                rsp->setServiceOptype(ServiceOptype::SERVICE_DISCOVERY);
                rsp->setMethod(msg->method());
                rsp->setHost(hosts);
                conn->send(rsp);
            }

        private:
            ProviderManager::ptr _providers;
            DiscoverManager::ptr _discovers;
        };
    }
}